﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Net;
using System.Net.Mail;
using System.Net.Mime;
using MySQL.Db;
using NLog;

namespace Gollie.Mail.Queue.Service
{
	public class MailSender
	{
		internal static readonly Logger logger = LogManager.GetCurrentClassLogger();
		private static int isAliveInterval = Convert.ToInt32(ConfigurationManager.AppSettings["isaliveinterval"]);
		private static DateTime lastLogTime = new DateTime(1980,1,1);

		internal static void SendAll()
		{
			DateTime now = DateTime.Now;
			TimeSpan dif = now - lastLogTime;
			bool touchLog = (dif.TotalSeconds > isAliveInterval);

			int unsentMails = Queries.CountUnsentMails();

			if (touchLog) {
				lastLogTime = now;
				logger.Info("{0} mails in queue", unsentMails);
			}

			if (unsentMails > 0)
			{
				logger.Info("Sending {0} queued mails...", unsentMails);

				string bccAddress = System.Configuration.ConfigurationManager.AppSettings["bccAddress"];

				ResultSet resultSet = Queries.GetUnsentMails();

				MailsSent sentMails = new MailsSent();

				using (SmtpClient smtpClient = new SmtpClient())
				{
					while (resultSet.Count > 0)
					{
						logger.Info("Sending {0} mails...", resultSet.Count);

						for (int i = 0; i < resultSet.Count; i++)
						{
							using (MailMessage msg = new MailMessage()) //'from' comes from smtp-settings in web.config
							{
								string recipient = resultSet.GetString(i, "recipient");
								string[] addresses = recipient.Split(new char[] { ',' });
								foreach (string address in addresses)
									msg.To.Add(address.Trim());

								msg.Subject = resultSet.GetString(i, "subject");
								msg.Body = resultSet.GetString(i, "txt");
								msg.SubjectEncoding = System.Text.Encoding.UTF8;
								msg.BodyEncoding = System.Text.Encoding.UTF8;
								msg.IsBodyHtml = true;

								if (string.IsNullOrEmpty(bccAddress) == false && resultSet.GetBool(i, "bcc"))
									msg.Bcc.Add(bccAddress);

								string att = resultSet.GetString(i, "attachment");
								if (att != null)
								{
									Attachment data = new Attachment(att, MediaTypeNames.Application.Octet);
									// Add time stamp information for the file.
									ContentDisposition disposition = data.ContentDisposition;
									disposition.CreationDate = System.IO.File.GetCreationTime(att);
									disposition.ModificationDate = System.IO.File.GetLastWriteTime(att);
									disposition.ReadDate = System.IO.File.GetLastAccessTime(att);
									msg.Attachments.Add(data);
								}

								try
								{
									logger.Info("Send mail to {0}, subject '{1}'...", recipient, msg.Subject);
									smtpClient.Send(msg);
									logger.Info("Success.");

									sentMails.Add(new MailSent(resultSet.GetUInt32(i, "id")));
								}
								catch (Exception e)
								{
									//if Send fails, leave the msg in the db. We will try again automatically in a while.
									logger.ErrorException("Error while sending mail to " + resultSet.GetString(i, "recipient"), e);

									//still update the status of the mails that were sent succesfully
									sentMails.UpDateDb();

									return;
								}
							}
						}

						sentMails.UpDateDb();

						resultSet = Queries.GetUnsentMails();
					}
				}
				logger.Info("Finished sending mails.");
			}
			
		}

		private class MailsSent : Collection<MailSent>
		{
			internal void UpDateDb()
			{
				if (this.Count == 0)
					return;

				MailSender.logger.Info("Setting mailstatus for {0} mails...", this.Count);

				QueryCollection queries = new QueryCollection(GollieDb.GollieMail);
				foreach (MailSent mail in this)
					Queries.SetMailStatusToSent(mail.DbId, mail.Sent, queries);

				try
				{
					queries.Execute();
				}
				catch (Exception e)
				{
					MailSender.logger.Warn("Set mail status to sent failed. Status unchanged. The error was: {0}", e.Message);
				}

				this.Clear();

				MailSender.logger.Info("Done.");
			}
		}

		private class MailSent
		{
			public MailSent(uint dbId)
			{
				this.DbId = dbId;
				this.Sent = DateTime.Now;
			}

			public uint DbId
			{
				get;
				private set;
			}

			public DateTime Sent
			{
				get;
				private set;
			}

		}
	}
}
